package com.xql.framework.filter;

import com.xql.framework.action.ActionFactory;
import com.xql.framework.action.ActionMapping;
import com.xql.framework.action.ActionSupport;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by apple on 15/3/22.
 * framework care filter
 */
public class XqlFrameworkFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        String methodName = req.getParameter("method");
        if (methodName == null || "".equals(methodName)) {
            methodName = "execute";
        }
        String actionName = getActionName(req);
        System.out.println("actionName=======" + actionName);
        ActionMapping actionMapping = ActionFactory.getActionMapping(actionName);
        //实例一Action对像
        if (ActionFactory.getAction(actionName) != null) {
            Object action = ActionFactory.getAction(actionName);
            Class clazz = action.getClass();
            //request parameter
            setFieldValue(req, action, clazz);
            Method executeAction = null;
            try {
                executeAction = clazz.getMethod(methodName);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            //ActionMapping 配置映射
            if (clazz.getGenericSuperclass() != null) {
                Class superClass = clazz.getSuperclass();
                String resultName = null;
                try {
                    Method setUrl = superClass.getMethod("setUrl", String.class);
                    resultName = (String) executeAction.invoke(action);
                    String url = req.getContextPath() + "/" + actionMapping.getResult(resultName).getPath();
                    setUrl.invoke(action, url);
                    Method setIsRedirect = superClass.getMethod("setRedirect", boolean.class);
                    setIsRedirect.invoke(action, actionMapping.getResult(resultName).isRedirect());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }

            ActionSupport actionSupport = (ActionSupport) action;
            //执行Action (Servlet)
            actionSupport.doPost(req, resp);
        } else {
            filterChain.doFilter(req, resp);
        }
    }

    /**
     * 把表单数据封装到Action的属性中
     * @param req
     * @param action
     * @param clazz
     */
    private void setFieldValue(HttpServletRequest req, Object action, Class clazz) {
        Map<String, String[]> paramMap = req.getParameterMap();
        Set<String> keySet = paramMap.keySet();
        keySet.remove("method");
        Field[] fields = clazz.getDeclaredFields();
        Iterator<String> iterator = keySet.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            for (Field f : fields) {
                try {
                    if (f.getName().equals(key)) {
                        f.setAccessible(true);
                        if (!f.getType().getName().endsWith("String")) {
                            Method fm = null;
                            convent(action, paramMap, f, key);
                        } else {
                            f.set(action, paramMap.get(key)[0]);
                        }
                        req.setAttribute(f.getName(), f.get(action));
                        iterator.remove();
                        f.setAccessible(false);
                    }

                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }

        System.out.println("end keySet.size==" + keySet.size());
        try {
            for (Field f : fields) {
                if (f.getType().getName().length() < 8 || f.getType().getName().startsWith("java.lang"))
                    continue;
                f.setAccessible(true);
                Object ob = f.getType().newInstance();
                System.out.println(ob.getClass().getName());
                forField(req, ob, paramMap, keySet);
                f.set(action, ob);
                req.setAttribute(f.getName(), ob);
                f.setAccessible(false);
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 给基本数据类型及其包装类赋值
     * @param action
     * @param paramMap
     * @param f
     * @param key
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     */
    private void convent(Object action, Map<String, String[]> paramMap, Field f, String key) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Method fm;
        if (f.getType().getName().equals("int")) {
            f.set(action, (Integer.valueOf(paramMap.get(key)[0])));
        } else if (f.getType().getName().equals("double")) {
            f.set(action, (Double.valueOf(paramMap.get(key)[0])));
        } else if (f.getType().getName().equals("short")) {
            f.set(action, (Short.valueOf(paramMap.get(key)[0])));
        } else if (f.getType().getName().equals("long")) {
            f.set(action, (Long.valueOf(paramMap.get(key)[0])));
        } else if (f.getType().getName().equals("boolean")) {
            f.set(action, (Boolean.valueOf(paramMap.get(key)[0])));
        } else if (f.getType().getName().equals("float")) {
            f.set(action, (Float.valueOf(paramMap.get(key)[0])));
        } else if(f.getType().getName().equals("[Ljava.lang.String;")){
            f.set(action, paramMap.get(key));
        }else{

            fm = f.getType().getDeclaredMethod("valueOf", String.class);
            f.set(action, (fm.invoke(f.getType(), paramMap.get(key)[0])));
        }
    }

    /**
     * 给Action的私有类对像赋值
     * @param req
     * @param ob
     * @param paramMap
     * @param keySet
     * @throws NoSuchFieldException
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    private void forField(HttpServletRequest req, Object ob, Map<String, String[]> paramMap, Set<String> keySet) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Field[] ffc = ob.getClass().getDeclaredFields();
        for (Iterator<String> iterator = keySet.iterator(); iterator.hasNext(); ) {
            String key = iterator.next();
            String[] params = paramMap.get(key);
            //System.out.println("key=>" + key + ":value=>" + params[0]);
            for (Field fc : ffc) {
                if (fc.getName().equals(key)) {
                    fc.setAccessible(true);
                    for (String param : params) {
                        if (!fc.getType().getName().endsWith("String")) {
                            convent(ob, paramMap, fc, key);
                        } else {
                            fc.set(ob, param);
                        }
                        fc.setAccessible(false);
                    }
                }
            }
        }
    }

    @Override
    public void destroy() {

    }

    private String getActionName(HttpServletRequest req) {
        return req.getServletPath().split("\\.")[0].substring(1);
    }
}
